#-*- coding: utf-8 -*-
'''
	Torrenter plugin for XBMC
	Copyright (C) 2011 Vadim Skorba
	vadim.skorba@gmail.com
	
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

#http://www.rasterbar.com/products/libtorrent/manual.html
import libtorrent
import time
import thread
import os.path
import urllib2
import hashlib
import xbmc
import xbmcgui
import Localization

class Torrent:
	torrentFile = None
	storageDirectory = ''
	torrentFilesDirectory = 'torrents'
	startPart = 0
	endPart = 0
	partOffset = 0
	torrentHandle = None
	session = None
	downloadThread = None
	threadComplete = False
	canPlay = False

	def __init__(self, storageDirectory = '', torrentFile = '', torrentFilesDirectory = 'torrents'):
		self.torrentFilesDirectory = torrentFilesDirectory
		self.storageDirectory = storageDirectory
		if not os.path.exists(self.storageDirectory + os.sep + self.torrentFilesDirectory):
			os.makedirs(self.storageDirectory + os.sep + self.torrentFilesDirectory)
		if os.path.exists(torrentFile):
			self.torrentFile = torrentFile
			self.torrentFileInfo = libtorrent.torrent_info(self.torrentFile)
		
	def saveTorrent(self, torrentUrl):
		torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + self.md5(torrentUrl) + '.torrent'
		try:
			request = urllib2.Request(torrentUrl)
			request.add_header('Referer', torrentUrl)
			localFile = open(torrentFile, "w+b")
			result = urllib2.urlopen(request)
			localFile.write(result.read())
			localFile.close()
		except:
			print 'Unable to save torrent file from "' + torrentUrl + '" to "' + torrentFile + '" in Torrent::saveTorrent'
			return
		if os.path.exists(torrentFile):
			self.torrentFileInfo = libtorrent.torrent_info(torrentFile)
			baseName = os.path.basename(self.getFilePath()).decode('utf-8').encode('ascii', 'ignore')
			newFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + baseName + '.torrent'
			if not os.path.exists(newFile):				
				try:
					os.rename(torrentFile, newFile)
				except:
					print 'Unable to rename torrent file from "' + torrentFile + '" to "' + newFile + '" in Torrent::renameTorrent'
					return
			self.torrentFile = newFile
			self.torrentFileInfo = libtorrent.torrent_info(self.torrentFile)

	def magnetToTorrent(self, magnet):
		session = libtorrent.session()
		session.start_dht()
		session.add_dht_router("router.bittorrent.com", 6881)
		session.add_dht_router("router.utorrent.com", 6881)
		session.add_dht_router("router.bitcomet.com", 6881)
		session.listen_on(6881, 6891)
		session.set_alert_mask(libtorrent.alert.category_t.storage_notification)
		magnetSettings = {
			'save_path': self.storageDirectory,
			'storage_mode': libtorrent.storage_mode_t(2),
			'paused': False,
			'auto_managed': True,
			'duplicate_is_error': True
		}
		handle = libtorrent.add_magnet_uri(session, magnet, magnetSettings)
		iterator = 0
		progressBar = xbmcgui.DialogProgress()
		progressBar.create(Localization.localize('Please Wait'), Localization.localize('Magnet-link is converting.'))
		while not handle.has_metadata():
			time.sleep(0.1)
			progressBar.update(iterator)
			iterator += 1
			if iterator == 100:
				iterator = 0
			if progressBar.iscanceled():
				progressBar.update(0)
				progressBar.close()
				return
		progressBar.update(0)
		progressBar.close()

		torrentInfo = handle.get_torrent_info()
		try:
			torrentFile = libtorrent.create_torrent(torrentInfo)
		except:
			xbmc.executebuiltin("Notification(%s, %s)" % (Localization.localize('Error'), Localization.localize('Your library out of date and can\'t use magnet-links.')))
			return
		
		baseName = os.path.basename(self.storageDirectory + os.sep + torrentInfo.files()[0].path).decode('utf-8').encode('ascii', 'ignore')
		self.torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + baseName + '.torrent'
		torentFileHandler = open(self.torrentFile, "wb")
		torentFileHandler.write(libtorrent.bencode(torrentFile.generate()))
		torentFileHandler.close()
		session.remove_torrent(handle)
		self.torrentFileInfo = libtorrent.torrent_info(self.torrentFile)

	def getFileSize(self, contentId = 0):
		return self.getContentList()[contentId].size

	def getFilePath(self, contentId = 0):
		return self.storageDirectory + os.sep + self.getContentList()[contentId].path

	def getContentList(self):
		return sorted(self.torrentFileInfo.files(), key=lambda fileName: fileName.path)

	def md5(self, string):
		hasher = hashlib.md5()
		hasher.update(string)
		return hasher.hexdigest()

	def downloadProcess(self, contentId):
		for part in range(self.startPart, self.endPart + 1):
			#print 'parts from ' + str(self.startPart) + ' to ' + str(self.endPart + 1) + ' now - ' + str(part)
			self.getPiece(part)
			time.sleep(0.1)
			self.checkThread()
			if part >= 1:
				self.canPlay = True
		self.threadComplete = True

	def startSession(self, contentId = 0):
		selectedFileInfo = self.getContentList()[contentId]
		self.partOffset = 30 * 1024 * 1024 / self.torrentFileInfo.piece_length()#30 MB
		#print 'partOffset ' + str(self.partOffset)
		self.startPart = selectedFileInfo.offset / self.torrentFileInfo.piece_length()
		self.endPart = (selectedFileInfo.offset + selectedFileInfo.size) / self.torrentFileInfo.piece_length()
		
		self.session = libtorrent.session()
		self.session.start_dht()
		self.session.add_dht_router("router.bittorrent.com", 6881)
		self.session.add_dht_router("router.utorrent.com", 6881)
		self.session.add_dht_router("router.bitcomet.com", 6881)
		self.session.listen_on(6881, 6891)
		self.session.set_alert_mask(libtorrent.alert.category_t.storage_notification)
		self.torrentHandle = self.session.add_torrent({'ti': self.torrentFileInfo, 'save_path': self.storageDirectory})
		for i in range(self.torrentFileInfo.num_pieces()):
			self.torrentHandle.piece_priority(i, 0)
		for i in range(self.startPart, self.startPart + self.partOffset):
			if i <= self.endPart:
				self.torrentHandle.piece_priority(i, 7)
		self.torrentHandle.set_sequential_download(True)
		thread.start_new_thread(self.downloadProcess, (contentId,))

	def fetchParts(self):
		priorities = self.torrentHandle.piece_priorities()
		status = self.torrentHandle.status()
		downloading = 0
		if len(status.pieces) == 0:
			return
		for part in range(self.startPart, self.endPart + 1):
			if priorities[part] != 0 and status.pieces[part] == False:
				self.checkThread()
				downloading += 1
		for part in range(self.startPart, self.endPart + 1):
			if priorities[part] == 0 and downloading < self.partOffset:
				self.checkThread()
				self.torrentHandle.piece_priority(part, 1)
				downloading += 1
		for part in range(self.startPart, self.endPart + 1):
			if priorities[part] != 0 and status.pieces[part] == False:
				self.checkThread()
				break

	def checkThread(self):
		if self.threadComplete == True:
			self.session.remove_torrent(self.torrentHandle)
			thread.exit()

	def getPiece(self, index):
		cache = {}
		if index in cache:
			result = cache[index]
			cache[index] = 0
			return result
		while True:
			status = self.torrentHandle.status()
			if len(status.pieces) == 0:
				break
			if status.pieces[index] == True:
				break
			time.sleep(0.5)
			self.checkThread()
		self.torrentHandle.read_piece(index)
		while True:
			part = self.session.pop_alert()
			if isinstance(part, libtorrent.read_piece_alert):
				if part.piece == index:
					return part.buffer
				else:
					cache[part.piece] = part.buffer
				break
			time.sleep(0.5)
			self.checkThread()
